// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/sender/audio_sender.h"

#include <stdint.h>

#include <memory>
#include <utility>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/values.h"
#include "media/base/fake_single_thread_task_runner.h"
#include "media/base/media.h"
#include "media/cast/cast_config.h"
#include "media/cast/cast_environment.h"
#include "media/cast/constants.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/cast_transport_impl.h"
#include "media/cast/test/utility/audio_utility.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace media {
namespace cast {

    namespace {

        void SaveOperationalStatus(OperationalStatus* out_status,
            OperationalStatus in_status)
        {
            DVLOG(1) << "OperationalStatus transitioning from " << *out_status << " to "
                     << in_status;
            *out_status = in_status;
        }

        class TransportClient : public CastTransport::Client {
        public:
            TransportClient() { }

            void OnStatusChanged(CastTransportStatus status) final
            {
                EXPECT_EQ(TRANSPORT_STREAM_INITIALIZED, status);
            };
            void OnLoggingEventsReceived(
                std::unique_ptr<std::vector<FrameEvent>> frame_events,
                std::unique_ptr<std::vector<PacketEvent>> packet_events) final {};
            void ProcessRtpPacket(std::unique_ptr<Packet> packet) final {};

            DISALLOW_COPY_AND_ASSIGN(TransportClient);
        };

    } // namespace

    class TestPacketSender : public PacketTransport {
    public:
        TestPacketSender()
            : number_of_rtp_packets_(0)
            , number_of_rtcp_packets_(0)
        {
        }

        bool SendPacket(PacketRef packet, const base::Closure& cb) final
        {
            if (IsRtcpPacket(&packet->data[0], packet->data.size())) {
                ++number_of_rtcp_packets_;
            } else {
                // Check that at least one RTCP packet was sent before the first RTP
                // packet.  This confirms that the receiver will have the necessary lip
                // sync info before it has to calculate the playout time of the first
                // frame.
                if (number_of_rtp_packets_ == 0)
                    EXPECT_LE(1, number_of_rtcp_packets_);
                ++number_of_rtp_packets_;
            }
            return true;
        }

        int64_t GetBytesSent() final { return 0; }

        void StartReceiving(
            const PacketReceiverCallbackWithStatus& packet_receiver) final { }

        void StopReceiving() final { }

        int number_of_rtp_packets() const { return number_of_rtp_packets_; }

        int number_of_rtcp_packets() const { return number_of_rtcp_packets_; }

    private:
        int number_of_rtp_packets_;
        int number_of_rtcp_packets_;

        DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
    };

    class AudioSenderTest : public ::testing::Test {
    protected:
        AudioSenderTest()
        {
            InitializeMediaLibrary();
            testing_clock_ = new base::SimpleTestTickClock();
            testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
            task_runner_ = new FakeSingleThreadTaskRunner(testing_clock_);
            cast_environment_ = new CastEnvironment(std::unique_ptr<base::TickClock>(testing_clock_),
                task_runner_, task_runner_, task_runner_);
            audio_config_.codec = CODEC_AUDIO_OPUS;
            audio_config_.use_external_encoder = false;
            audio_config_.rtp_timebase = kDefaultAudioSamplingRate;
            audio_config_.channels = 2;
            audio_config_.max_bitrate = kDefaultAudioEncoderBitrate;
            audio_config_.rtp_payload_type = RtpPayloadType::AUDIO_OPUS;

            transport_ = new TestPacketSender();
            transport_sender_.reset(new CastTransportImpl(
                testing_clock_, base::TimeDelta(), base::MakeUnique<TransportClient>(),
                base::WrapUnique(transport_), task_runner_));
            OperationalStatus operational_status = STATUS_UNINITIALIZED;
            audio_sender_.reset(new AudioSender(
                cast_environment_,
                audio_config_,
                base::Bind(&SaveOperationalStatus, &operational_status),
                transport_sender_.get()));
            task_runner_->RunTasks();
            CHECK_EQ(STATUS_INITIALIZED, operational_status);
        }

        ~AudioSenderTest() override { }

        base::SimpleTestTickClock* testing_clock_; // Owned by CastEnvironment.
        TestPacketSender* transport_; // Owned by CastTransport.
        std::unique_ptr<CastTransportImpl> transport_sender_;
        scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
        std::unique_ptr<AudioSender> audio_sender_;
        scoped_refptr<CastEnvironment> cast_environment_;
        FrameSenderConfig audio_config_;
    };

    TEST_F(AudioSenderTest, Encode20ms)
    {
        const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(20);
        std::unique_ptr<AudioBus> bus(
            TestAudioBusFactory(audio_config_.channels, audio_config_.rtp_timebase,
                TestAudioBusFactory::kMiddleANoteFreq, 0.5f)
                .NextAudioBus(kDuration));

        audio_sender_->InsertAudio(std::move(bus), testing_clock_->NowTicks());
        task_runner_->RunTasks();
        EXPECT_LE(1, transport_->number_of_rtp_packets());
        EXPECT_LE(1, transport_->number_of_rtcp_packets());
    }

    TEST_F(AudioSenderTest, RtcpTimer)
    {
        const base::TimeDelta kDuration = base::TimeDelta::FromMilliseconds(20);
        std::unique_ptr<AudioBus> bus(
            TestAudioBusFactory(audio_config_.channels, audio_config_.rtp_timebase,
                TestAudioBusFactory::kMiddleANoteFreq, 0.5f)
                .NextAudioBus(kDuration));

        audio_sender_->InsertAudio(std::move(bus), testing_clock_->NowTicks());
        task_runner_->RunTasks();

        // Make sure that we send at least one RTCP packet.
        base::TimeDelta max_rtcp_timeout = base::TimeDelta::FromMilliseconds(1 + kRtcpReportIntervalMs * 3 / 2);
        testing_clock_->Advance(max_rtcp_timeout);
        task_runner_->RunTasks();
        EXPECT_LE(1, transport_->number_of_rtp_packets());
        EXPECT_LE(1, transport_->number_of_rtcp_packets());
    }

} // namespace cast
} // namespace media
